/************************************************
 * MirrorOS Kernel Drivers
 * RamFS Driver
 * Copyright 2008 Matthew Iselin
 ***********************************************/

// Defines

// seeking stuff
#define SEEK_SET		0
#define SEEK_CUR		1
#define SEEK_END		2

// Prototypes

int dprintf( const char* fmt, ... );

void InstallDevice(	char* prefix,
					unsigned int s_open,
					unsigned int s_read,
					unsigned int s_write,
					unsigned int s_seek,
					unsigned int s_ioctl,
					unsigned int s_stat,
					unsigned int s_close,
					int blocksize
					);

// Types

// struct stat stuff
#ifdef __CYGWIN__
#include <cygwin/stat.h>
#ifdef _COMPILING_NEWLIB
#define stat64 __stat64
#endif
#else
struct	stat
{
  short		st_dev;
  unsigned short		st_ino;
  unsigned int	st_mode;
  unsigned short	st_nlink;
  unsigned short		st_uid;
  unsigned short		st_gid;
  short		st_rdev;
  long		st_size;
  /* SysV/sco doesn't have the rest... But Solaris, eabi does.  */
#if defined(__svr4__) && !defined(__PPC__) && !defined(__sun__)
  unsigned long	st_atime;
  unsigned long	st_mtime;
  unsigned long	st_ctime;
#else
  unsigned long	st_atime;
  long		st_spare1;
  unsigned long	st_mtime;
  long		st_spare2;
  unsigned long	st_ctime;
  long		st_spare3;
  long		st_blksize;
  long		st_blocks;
  long	st_spare4[2];
#endif
};
#endif

// a local descriptor
struct fd
{
	int avail;
	unsigned int memoffset;
	unsigned int maxsz;
	unsigned int offset;
};
volatile struct fd desctable[512];

// ramdisk stuff

struct ramdisk_head {
	unsigned int total_size; // total ramdisk data section size
	unsigned int numfiles; // total number of files
	unsigned int firstfile; // location of the first file (in data area)
	unsigned int filetab; // location of the file table
};

struct ramdisk_filent {
	char name[256]; // name of the file
	unsigned int offset; // offset in the binary
	unsigned int size; // size of the file
};

// Variables

// the base address of the ramfs binary
unsigned int base = 0;

// Functions

// opens a file
int ramfs_open( const char* path, int flags )
{
	dprintf("ramfs_open [%x]\n", base);
	
	// check for a valid base
	if( base == 0 )
		return 512; // must have a above-zero descriptor, but make it invalid for all I/O other than ioctl

	// find a space in the fd table
	int i;
	for( i = 0; i < 512; i++ )
		if( desctable[i].avail )
			break;

	// not successful?
	if( i == 512 )
		return -1;

	// grab the file table
	struct ramdisk_head* header = (struct ramdisk_head*) base;
	dprintf("%d files\n", header->numfiles);
	struct ramdisk_filent* ftab = (struct ramdisk_filent*) (base + header->filetab);

	// search for the file, o
	int z;
	for( z = 0; z < header->numfiles; z++ )
	{
		// is it the same?
		dprintf("checking %s against %s\n", ftab[z].name, path);
		if( strncmp( path + strlen( "/ramfs/" ) /* skip the "/ramfs/" */, ftab[z].name, strlen( path + strlen( "/ramfs/" ) ) ) == 0 )
			break;
	}

	// was it not found?
	if( z == header->numfiles )
		return -1;

	// set it as unavailable
	desctable[i].avail = 0;
	desctable[i].memoffset = base + ftab[z].offset;
	desctable[i].maxsz = ftab[z].size;
	desctable[i].offset = 0;

	// return the descriptor
	return i;
}

// reads from a file
unsigned long ramfs_read( int fd, void* buff, unsigned long count )
{
	// verify the descriptor
	if( fd < 0 || fd >= 512 )
		return 0;

	// truncate the count if needed
	if( count > desctable[fd].maxsz )
		count = desctable[fd].maxsz;
	if( (count + desctable[fd].offset) > desctable[fd].maxsz )
		count = desctable[fd].offset - count;
	
	// zero count?
	if( count == 0 )
		return 0; // no data read

	// read in the data
	char* tmp = (char*) buff;
	char* file = (char*) (desctable[fd].memoffset);
	unsigned long i;
	for( i = 0; i < count; i++ )
	{
		tmp[i] = file[desctable[fd].offset];
		desctable[fd].offset++;
	}
	tmp[i] = 0;

	// return the number of bytes read
	return i - 1;
}

// seeks within the file
unsigned long ramfs_seek( int fd, unsigned long off, int set )
{
	// verify the descriptor
	if( fd < 0 || fd >= 512 )
		return 0;
	
	dprintf( "ramfs_seek\n" );
	
	// what's the set?
	switch( set )
	{
		case SEEK_SET:
			if( off > desctable[fd].maxsz )
				off = desctable[fd].maxsz;
			desctable[fd].offset = off;
			break;
		case SEEK_CUR:
			desctable[fd].offset += off;
			if( desctable[fd].offset > desctable[fd].maxsz )
				desctable[fd].offset = desctable[fd].maxsz;
			break;
		case SEEK_END:
			desctable[fd].offset = desctable[fd].maxsz - off;
			if( off > desctable[fd].maxsz )
				desctable[fd].offset = desctable[fd].memoffset;
			break;
	}
	
	// return the new offset
	return desctable[fd].offset;
}

// controls the driver itself (in this driver anyway)
int ramfs_ioctl( int fd, int action, int data )
{
	// always allow setting the base (the ramfs is only used at startup, before malicious programs are loaded)
	if( action == 1 && fd == 512 )
	{
		base = data;
		dprintf("set!\n");
	}
}

// gets information about a specific open descriptor
int ramfs_stat( int fd, struct stat* buf )
{
	// verify the descriptor
	if( fd < 0 || fd >= 512 )
		return -1;
	
	// yes, fill the buffer (some things get filled by the caller, we fill what we can)
	buf->st_size = desctable[fd].maxsz;
	buf->st_blocks = buf->st_size;

	// success, no failure
	return 0;
}

// closes a descriptor
int ramfs_close( int fd )
{
	// verify the descriptor
	if( fd < 0 || fd >= 512 )
		return -1;
	
	// close the file (basically make it available again)
	desctable[fd].avail = 1;
}

void dmain( int argc, char* argv[] )
{
	// setup the descriptor table
	int i;
	for( i = 0; i < 512; i++ )
		desctable[i].avail = 1;
	
	dprintf("dmain for RAMFS!!!!!\n");

	// install the device calls
	InstallDevice( "/ramfs/", (unsigned int) ramfs_open, (unsigned int) ramfs_read, 0, (unsigned int) ramfs_seek, (unsigned int) ramfs_ioctl, (unsigned int) ramfs_stat, (unsigned int) ramfs_close, 1 );
}
